package org.longs.converter;

import lombok.Getter;
import lombok.Setter;
import org.longs.bean.Frame;
import org.longs.bean.ImageFile;
import org.longs.bean.PalRGB;
import org.longs.bean.WasHeader;
import org.longs.config.Constant;
import org.longs.converter.parser.Parser;
import org.longs.converter.parser.ParserManager;
import org.longs.converter.parser.Recorder;
import org.longs.stream.LittleAccessFile;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Getter
public class WasConverter extends BaseConverter {

    private WasHeader header;

    private PalRGB[] palRGBs;


    @Setter
    private String name;

    @Setter
    private List<ImageFile> imageFiles;


    public WasConverter(LittleAccessFile randomAccessFile) {
        super(randomAccessFile);
    }

    public WasConverter(String filePath) {
        super(filePath);
    }

    @Override
    public String getFlag() {
        return Constant.WAS_FLAG;
    }

    @Override
    public void analysisBody(LittleAccessFile is) throws IOException {
        header = analysisHead(is);
        palRGBs = analysisRPG();
        imageFiles = analysisImageFile(is);
    }


    public byte[] analysisSpaces(ImageFile.Spaces spaces) throws IOException {
        LittleAccessFile is = getRandomAccessFile();
        is.seek(getOffset());
        is.skipBytes(spaces.getOffset());
        byte[] bytes = new byte[spaces.getSize()];
        is.readFully(bytes);
        return bytes;
    }

    public Frame analysisFrame(ImageFile imageFile) throws IOException {
        if (imageFile.getSize() == 0 || imageFile.getOffset() == 0) {
            imageFile.setFrame(new Frame());
            return imageFile.getFrame();
        }
        LittleAccessFile is = getRandomAccessFile();
        is.seek(getOffset());
        is.skipBytes(imageFile.getOffset());
        Frame frame = new Frame();
        frame.setKeyX(is.readLittleInt());
        frame.setKeyY(is.readLittleInt());
        frame.setWidth(is.readLittleInt());
        frame.setHeight(is.readLittleInt());
        imageFile.setFrame(frame);
        return frame;
    }

    public List<ImageFile.Spaces> analysisSpacesLists(ImageFile imageFile) throws IOException {
        if (imageFile.getOffset() == 0 || imageFile.getSize() == 0) {
            return null;
        }
        LittleAccessFile is = getRandomAccessFile();
        List<ImageFile.Spaces> spacesLists = new ArrayList<>();
        ImageFile.Spaces spaces;
        Frame frame = imageFile.getFrame();
        if (frame == null) {
            frame = analysisFrame(imageFile);
        } else {
            is.seek(getOffset());
            is.skipBytes(imageFile.getOffset() + 16);
        }
        if (frame.getHeight() == 0 || frame.getWidth() == 0) {
            return null;
        }
        int size = 0;
        int widthOffset;
        for (int i = 0; i < frame.getHeight(); i++) {
            widthOffset = is.readLittleInt();
            if (widthOffset == 1) {
                size = is.readLittleInt() + imageFile.getOffset();
            }
            int offset = widthOffset + imageFile.getOffset();
            if (!spacesLists.isEmpty()) {
                spaces = spacesLists.get(spacesLists.size() - 1);
                spaces.setSize(offset - spaces.getOffset());
            }
            spaces = new ImageFile.Spaces();
            spaces.setOffset(offset);
            spacesLists.add(spaces);
        }
        spaces = spacesLists.get(spacesLists.size() - 1);
        if (size != 0) {
            spaces.setSize(size - spaces.getOffset());
        } else {
            spaces.setSize((imageFile.getOffset() + imageFile.getSize()) - spaces.getOffset());
        }
        imageFile.setSpacesList(spacesLists);
        return spacesLists;
    }


    private List<ImageFile> analysisImageFile(LittleAccessFile is) throws IOException {
        List<ImageFile> imageFileList = new ArrayList<>();
        int offset;
        int frame = header.getFrame();
        for (int groupIndex = 0; groupIndex < header.getGroup(); groupIndex++) {
            for (int frameIndex = 0; frameIndex < frame; frameIndex++) {
                int anInt = is.readLittleInt();
                if (anInt == 0) {
                    ImageFile imageFile = new ImageFile();
                    imageFile.setFrame(new Frame());
                    imageFile.setSpacesList(new ArrayList<>());
                    imageFile.setHash(groupIndex + "-" + frameIndex);
                    imageFileList.add(imageFile);
                    continue;
                }
                offset = anInt + header.getHLen() + 4;
                if (!imageFileList.isEmpty()) {
                    ImageFile imageFile = imageFileList.get(imageFileList.size() - 1);
                    imageFile.setSize(offset - imageFile.getOffset());
                    if (imageFile.getSize() == 16) {
                        imageFileList.remove(imageFile);
                    }
                }
                ImageFile imageFile = new ImageFile();
                imageFile.setHash(groupIndex + "-" + frameIndex);
                imageFile.setOffset(offset);
                imageFileList.add(imageFile);
            }
        }
        ImageFile imageFile = imageFileList.get(imageFileList.size() - 1);
        imageFile.setSize((int) (getTotal() - imageFile.getOffset()));
        return imageFileList;
    }

    private PalRGB[] analysisRPG() throws IOException {
        LittleAccessFile is = getRandomAccessFile();
        is.seek(getOffset());
        is.skipBytes(this.header.getHLen() + 4);
        PalRGB[] palRGBs = new PalRGB[256];
        for (int i = 0; i < palRGBs.length; i++) {
            palRGBs[i] = new PalRGB(is.readLittleShort());
        }
        return palRGBs;
    }

    private WasHeader analysisHead(LittleAccessFile is) throws IOException {
        WasHeader headerBean = new WasHeader();
        headerBean.setFlag(getFlag());
        headerBean.setHLen(is.readLittleShort());
        headerBean.setGroup(is.readLittleShort());
        headerBean.setFrame(is.readLittleShort());
        headerBean.setWidth(is.readLittleShort());
        headerBean.setHeight(is.readLittleShort());
        headerBean.setKeyX(is.readLittleShort());
        headerBean.setKeyY(is.readLittleShort());
        return headerBean;
    }

    public BufferedImage converterBufferedImage(ImageFile imageFile) throws IOException {
        if (imageFile.getBufferedImage() != null) {
            return imageFile.getBufferedImage();
        }
        List<ImageFile.Spaces> spacesList = imageFile.getSpacesList();
        Frame frame = imageFile.getFrame();
        if (frame == null) {
            frame = analysisFrame(imageFile);
        }
        if (spacesList == null) {
            spacesList = analysisSpacesLists(imageFile);
        }
        if (palRGBs == null) {
            palRGBs = analysisRPG();
        }
        BufferedImage bufferedImage;
        if (frame.getWidth() == 0 || frame.getHeight() == 0) {
            bufferedImage = new BufferedImage(header.getWidth(), header.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
            frame.setKeyY(header.getKeyY());
            frame.setKeyX(header.getKeyX());
            imageFile.setBufferedImage(bufferedImage);
            return bufferedImage;
        } else {
            bufferedImage = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        }
        Recorder recorder = new Recorder();
        recorder.setPalRGBs(palRGBs);
        for (ImageFile.Spaces spaces : spacesList) {
            recorder.loop(analysisSpaces(spaces), bufferedImage);
        }
        imageFile.setBufferedImage(bufferedImage);
        return bufferedImage;
    }

}
